home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / opengl / xlib / bufferogl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  19.7 KB  |  549 lines

  1. /*
  2.  * Copyright 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.  * bufferogl:  an openGL-Xlib version of Yusuf Attarwala's Motif-based 4.0
  19.  *             mixed model "bufferglm.c" demo program.  This is the "after"
  20.  *             version, ported from its "before" counterpart, located at
  21.  *             ../../GLX/buffer/bufferglx.c
  22.  *             
  23.  *             bufferogl implements an openGL methodology for creating and
  24.  *             then switching back-and-forth between single and double 
  25.  *             -buffer visuals or drawing modes.
  26.  * 
  27.  *       left mouse  :  switch to single buffer
  28.  *       middle mouse:  switch to double buffer
  29.  *       right mouse :  animate the green sphere in the current buffer mode
  30.  *       ESC key     :  exits the program
  31.  *
  32.  *       the program handles redraw (ConfigureNotify--move and resize) and
  33.  *       expose (pop, expose, de-iconify) and button (i.e. mouse) events.
  34.  *                                      ratmandu -- ported July, 1993   
  35.  */
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #include <GL/glx.h>
  39. #include <GL/gl.h>
  40. #include <GL/glu.h>
  41. #include <X11/Xlib.h>
  42. #include <X11/Xutil.h>
  43. #include <X11/keysym.h>
  44.  
  45. #define top     0                            /* parent X window array index */
  46. #define SBglwin 1                    /* singlebuffer'd RGBA GL window index */
  47. #define DBglwin 2                    /* doublebuffer'd RGBA GL window index */
  48. #define WINMAX  3                               /* number of unique windows */
  49.  
  50. #define Mat1 1                       /* we'll use two material definitions, */
  51. #define Mat2 2                       /* for lighting, one for each sphere   */
  52.  
  53. static void openwindow(char *);
  54. void Winset(int);
  55. static void resize_buffer(long);
  56. static void clean_exit(void);
  57. void initGL(void);
  58. void setMatrix(void);
  59. void loop(void);
  60. void drawScene(void);
  61.  
  62. Display *dpy;                         /* The X server connection            */
  63. Atom del_atom;                        /* for possibility of WM killing app  */
  64. Window glwins[WINMAX];                /* array for SB/DB window ids         */
  65. GLXContext glcontexts[WINMAX];        /* array for SB/DB window context ids */
  66. float ax, ay, az;                     /* angles green sphere twirls on      */
  67. int xsize, ysize;                     /* current size-of-window keepers     */
  68. long buffermode;                      /* flag for cur win (single||double)  */
  69. XEvent event;                         /* needs to be global for XIfEvent    */
  70. GLUquadricObj *quadObj;               /* used to make the two spheres with  */
  71.  
  72.  
  73. main(argc,argv)
  74. int argc;
  75. char **argv;
  76. {
  77.     KeySym keysym;                              /* for catching             */
  78.     char buf[4];                                /* keyboard events          */
  79.     int myExpose, myConfigure, 
  80.         myButtRelease, myKeyPress;              /* store which events occur */
  81.  
  82.  
  83.     myExpose = myConfigure = myButtRelease = myKeyPress = GL_FALSE;
  84.  
  85.     openwindow(argv[0]);
  86.    
  87.    /* initialize *both* windows just as we wud do with winopen/winset */
  88.     Winset(DBglwin);
  89.     initGL();                                          /* do GL init stuff */
  90.     setMatrix();
  91.   
  92.    /* start out making the singlebuffer window be our current GL window */
  93.     Winset(SBglwin);
  94.     initGL();                                          /* do GL init stuff */
  95.     setMatrix();
  96.     drawScene();    /* and begin by drawing the scene in singlebuffer mode */
  97.  
  98.  
  99.     /*
  100.      * The event loop.
  101.      */
  102.     while (1) {         /* standard logic:  get event(s), process event(s) */
  103.  
  104.         glFlush();                        /* For proper DGL performance */
  105.  
  106.     /* this "do while" loop does the `get events' half of the "get events,
  107.      *  process events" action of the infinite while.  this is to ensure
  108.      *  the event queue is always drained before the events that have come
  109.      *  in are processed.
  110.      */
  111.         do {
  112.  
  113.             XNextEvent(dpy, &event);
  114.                 switch (event.type) {
  115.  
  116.             /* "Expose" events are sort of like "REDRAW" in gl-speak in
  117.              *  terms of when a window becomes visible, or a previously
  118.              *  invisible part becomes visible.
  119.              */
  120.                 case Expose:                                   /* Exposures */
  121.                     myExpose = GL_TRUE;
  122.                     break;
  123.  
  124.  
  125.             /* "ConfigNotify" events are like "REDRAW" in terms of changes
  126.              *   to a window's size or position.
  127.              */
  128.                 case ConfigureNotify:                 /* Resize GL manually */
  129.                     if (event.xconfigure.window == glwins[top]) {
  130.             /* save the changed width/height of the parent X window */
  131.                         xsize = event.xconfigure.width;
  132.                         ysize = event.xconfigure.height;
  133.                         myConfigure = GL_TRUE;
  134.                     } 
  135.                     break;
  136.  
  137.  
  138.             /* Wait for "ButtonRelease" events so the queue doesn't fill up
  139.              *  the way it wud if the user sits on ButtonPresss.
  140.              */
  141.                 case ButtonRelease:                        
  142.                     if (event.xbutton.button == Button1) {   /* LEFTMOUSE:  */
  143.                         buffermode = SBglwin;                /* switch to   */
  144.                         myButtRelease = GL_TRUE;             /* singlebuffer*/
  145.                     } else if (event.xbutton.button == Button2) {
  146.                         buffermode = DBglwin;       /* MIDDLEMOUSE:  switch */
  147.                         myButtRelease = GL_TRUE;    /* to doublebuffer mode */
  148.                     } else if (event.xbutton.button == Button3) {
  149.                         loop();                             /* RIGHTMOUSE:  */
  150.                     }                                 /* twirl green sphere */
  151.                     break;
  152.  
  153.  
  154.             /* "ClientMessage" is generated if the WM itself is being
  155.              *  gunned down and sends an exit signal to any running prog.
  156.              */
  157.                 case ClientMessage:
  158.                     if (event.xclient.data.l[0] == del_atom)
  159.                         clean_exit();
  160.                     break;
  161.  
  162.  
  163.             /* "KeyPress" events are those that would be generated before
  164.              *   whenever queueing up any KEYBD key via qdevice.
  165.              */
  166.                 case KeyPress:
  167.                    /* save out which unmodified key (i.e. the  key was
  168.                     *  not modified w/something like "Shift", "Ctrl",
  169.                     *  or "Alt") got pressed for use below.
  170.                     */
  171.                     XLookupString((XKeyEvent *)&event, buf, 4, &keysym, 0);
  172.                     myKeyPress = GL_TRUE;
  173.                     break;
  174.  
  175.               }  /* end switch (event.type) */
  176.  
  177.  
  178.         } while (XPending(dpy));   /* end "do { } while".
  179.                                     * XPending() is like qtest()--it only
  180.                                     * tells you if there're any events
  181.                                     * presently in the queue.  it does not
  182.                                     * disturb queue's contents in any way.
  183.                                     */
  184.  
  185.     /* On an "Expose" event, redraw the affected pop'd or de-iconized window
  186.      */
  187.         if (myExpose) {
  188.             drawScene();                               /* draw the GL stuff */
  189.             myExpose = GL_FALSE;             /* reset flag--queue now empty */
  190.         }
  191.  
  192.     /* On a "ConfigureNotify" event, the GL window has either been moved or
  193.      *  resized.  Respond accordingly and then redraw its contents.
  194.      */
  195.         if (myConfigure) {
  196.             if (buffermode == SBglwin) {
  197.                 resize_buffer(DBglwin);      /* update viewport & CTM stuff */
  198.                 resize_buffer(SBglwin);      /* for *both* windows and then */
  199.             } else if (buffermode == DBglwin) {
  200.                 resize_buffer(SBglwin);
  201.                 resize_buffer(DBglwin);
  202.             } 
  203.             drawScene();
  204.             myConfigure = GL_FALSE;          /* reset flag--queue now empty */
  205.         }
  206.  
  207.     /* On a "ButtonRelease" event, the flag gets set only for the LEFTMORE
  208.      *  or MIDDLEMOUSE.  we need to change the buffermode state in this case.
  209.      *  In either case, XStoreName updates the window's title bar, Winset
  210.      *  then switches to the current buffermode graphics context, and we
  211.      *  Map the current window *BEFORE* we unmap the previous one.  this
  212.      *  alleviates a painful "flicker" that occurs if you unmap the previous
  213.      *  window first.
  214.      */
  215.         if (myButtRelease) {
  216.             if (buffermode == SBglwin) {
  217.                 XStoreName(dpy,glwins[top],
  218.                    "single[LEFTMOUSE]-buffer GL window");
  219.         XRaiseWindow(dpy, glwins[SBglwin]);
  220.                 Winset(SBglwin);
  221.             } else if (buffermode == DBglwin) {
  222.                 XStoreName(dpy,glwins[top],
  223.                    "double[RIGHTMOUSE]-buffer GL window");
  224.         XRaiseWindow(dpy, glwins[DBglwin]);
  225.                 Winset(DBglwin);
  226.             }
  227.             myButtRelease = GL_FALSE;
  228.         }
  229.  
  230.         /* On a keypress of Esc key, exit program.
  231.          */
  232.         if (myKeyPress) {
  233.             if (keysym == XK_Escape)
  234.                 clean_exit();
  235.         }
  236.  
  237.     }      /* end while(1) */
  238.  
  239. }     /* end main */
  240.  
  241.  
  242.  
  243. static int SBattributeList[] = { GLX_RGBA, 
  244.                  GLX_DEPTH_SIZE, 1, 
  245.                  None };
  246. static int DBattributeList[] = { GLX_RGBA, 
  247.                  GLX_DOUBLEBUFFER, 
  248.                  GLX_DEPTH_SIZE, 1, 
  249.                  None };
  250.  
  251. /* WaitForNotify:
  252.  *   used to make sure the MapWindow() calls inside openwindow() occur
  253.  *   beFORE glXMakeCurrent() is invoked so as to avoid a race condition.
  254.  */
  255. static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
  256.     return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
  257. }
  258.  
  259.  
  260. /*  openwindow -
  261.  *     establish connection to X server, get screen info, specify the
  262.  *     attributes we want the WM to try to provide, and create the 2
  263.  *     GL windows--one single and one double -buffered--that we'll employ.
  264.  */
  265. static void openwindow(char *progname) {
  266.  
  267.     XSizeHints Winhints;                          /* used to fix window size */
  268.     XSetWindowAttributes SBswa, DBswa;
  269.     XColor gray;
  270.     XVisualInfo *SBvi, *DBvi;
  271.     Colormap SBcmap, DBcmap;
  272.     int scrnnum;                              /* X screen number            */
  273.     int xorig, yorig;
  274.     long scrnheight;                          /* for defining maximum resize*/
  275.  
  276.  
  277.  
  278.   /* Connect to the X server and get screen info */
  279.     if ((dpy = XOpenDisplay(NULL)) == NULL) {
  280.         fprintf(stderr, "%s: cannot connect to X server %s\n",
  281.                                  progname, XDisplayName(NULL));
  282.         exit(1);
  283.     }
  284.     scrnnum = DefaultScreen(dpy);
  285.     scrnheight = DisplayHeight(dpy, scrnnum);
  286.  
  287.     xorig = 0;  yorig = 0;                    /* define window initial size */
  288.     xsize = 300; ysize = 300;
  289.  
  290.    /* first create the top level X window which will be the parent to both
  291.     * the singlebuffer and doublebuffer rendering windows
  292.     */
  293.     glwins[top] = XCreateSimpleWindow(dpy, RootWindow(dpy, scrnnum),
  294.                       xorig, yorig, xsize, ysize, 0, 0, 0);
  295.     if (!(glwins[top])) {
  296.         fprintf(stderr,"%s: couldn't create \"parent\" X window\n",progname);
  297.     exit(1);
  298.     }
  299.    /* including this call to XSelectInput shud not be necessary, but there's
  300.     * a bug, so we have to resort to this hackaround.
  301.     */
  302.     XSelectInput(dpy, glwins[top],
  303.           StructureNotifyMask|ButtonPressMask|ButtonReleaseMask|KeyPressMask);
  304.  
  305.    /* define the string that will show up in the window title bar (and icon) */
  306.     XStoreName(dpy,glwins[top],"single/double -buffer program");
  307.  
  308.    /* specify the values for the Window Size Hints we want to enforce:  this
  309.     *  window's aspect ratio needs to stay at 1:1, constrain min and max
  310.     *  window size, and specify the initial size of the window.
  311.     */
  312.     Winhints.width  = xsize;          /* specify desired x/y size of window */
  313.     Winhints.height = ysize;
  314.     Winhints.min_width = xsize;                       /* define min and max */
  315.     Winhints.max_width = scrnheight-1;                /* width and height   */
  316.     Winhints.min_height = ysize;
  317.     Winhints.max_height = scrnheight-1;
  318.     Winhints.min_aspect.x = 1;                /* keep aspect at a 1:1 ratio */
  319.     Winhints.max_aspect.x = 1;
  320.     Winhints.min_aspect.y = 1;
  321.     Winhints.max_aspect.y = 1;
  322.                                              /* set the corresponding flags */
  323.     Winhints.flags = USSize|PMaxSize|PMinSize|PAspect;
  324.     XSetNormalHints(dpy, glwins[top], &Winhints);
  325.  
  326.  
  327.    /* now define and create the single and doublebuffer rendering windows */
  328.  
  329.   /* first get the appropriate visuals */
  330.     SBvi = glXChooseVisual(dpy, scrnnum, SBattributeList);
  331.     DBvi = glXChooseVisual(dpy, scrnnum, DBattributeList);
  332.  
  333.   /* create the appropriate  GLX contexts */
  334.     glcontexts[SBglwin] = glXCreateContext(dpy, SBvi, 0, GL_TRUE);
  335.     glcontexts[DBglwin] = glXCreateContext(dpy, DBvi, 0, GL_TRUE);
  336.  
  337.   /* now create the two colormaps */
  338.     SBcmap = XCreateColormap(dpy, RootWindow(dpy, SBvi->screen),
  339.                            SBvi->visual, AllocNone);
  340.     DBcmap = XCreateColormap(dpy, RootWindow(dpy, DBvi->screen),
  341.                            DBvi->visual, AllocNone);
  342.  
  343.   /* now create both windows */
  344.     SBswa.colormap = SBcmap;
  345.     SBswa.border_pixel = 0;
  346.     SBswa.event_mask = StructureNotifyMask | ButtonReleaseMask | 
  347.        ButtonPressMask | KeyPressMask;    /* express interest in events */
  348.     glwins[SBglwin] = XCreateWindow(dpy, glwins[top],
  349.                       xorig, yorig, xsize, ysize,
  350.                       0, SBvi->depth, InputOutput, SBvi->visual,
  351.                       CWBorderPixel|CWColormap|CWEventMask, &SBswa);
  352.     DBswa.colormap = DBcmap;
  353.     DBswa.border_pixel = 0;
  354.     DBswa.event_mask = StructureNotifyMask  | ButtonReleaseMask | 
  355.        ButtonPressMask | KeyPressMask;    /* express interest in events */
  356.     glwins[DBglwin] = XCreateWindow(dpy, glwins[top],
  357.                       xorig, yorig, xsize, ysize,
  358.                       0, DBvi->depth, InputOutput, DBvi->visual,
  359.                       CWBorderPixel|CWColormap|CWEventMask, &DBswa);
  360.  
  361.   /* now map both windows, but leave the singlebuffer window on top */
  362.     XMapWindow(dpy, glwins[SBglwin]);
  363.     XMapWindow(dpy, glwins[DBglwin]);
  364.     XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[DBglwin]);
  365.     XRaiseWindow(dpy, glwins[SBglwin]);
  366.     XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[SBglwin]);
  367.     XMapWindow(dpy, glwins[top]);
  368.  
  369.   /* connect the context to the window */
  370.     glXMakeCurrent(dpy, glwins[SBglwin], glcontexts[SBglwin]);
  371.  
  372.   /* ensure the GL colormap is installed for this app */
  373.     XSetWMColormapWindows(dpy, glwins[top], glwins, WINMAX);
  374.  
  375.   /* express interest in WM being able to kill this app */
  376.     if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", True)) != None)
  377.         XSetWMProtocols(dpy, glwins[top], &del_atom, 1);
  378.  
  379.     glFlush();
  380. }
  381.  
  382.  
  383. char *typeToName[] = {
  384.     "parent window",
  385.     "RGB single buffer",
  386.     "RGB double buffer",
  387. };
  388.  
  389.  
  390. /* A little helper wrapper for glXMakeCurrent.
  391.  *  passes the index to jointly access the windows and contexts array and
  392.  *  checks the return value.  This makes the call to begin GL drawing a
  393.  *  little simpler.  Building in such automatic error checking is always a
  394.  *  "smooth move" (*not* like the cancerously-mutant human with the enlarged
  395.  *  proboscis plastered all over the place urging people to be likewise 
  396.  *  smoothly cancerous and hardly cool).
  397.  */
  398. void Winset(int type)
  399. {
  400.     int rv;
  401.  
  402.     /*
  403.     XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[type]);
  404.     */
  405.     XSync(dpy,GL_FALSE);
  406.     rv = glXMakeCurrent(dpy, glwins[type], glcontexts[type]);
  407.     if (rv == GL_FALSE) {
  408.         fprintf(stderr, "glXMakeCurrent failed for an %s-type window\n", 
  409.                                             typeToName[type]);
  410.         exit(-1);
  411.     }
  412. }
  413.  
  414.  
  415. /*  window has been moved or resized so update viewport & CTM stuff.
  416.  */
  417. static void resize_buffer(long wintype) {
  418.  
  419.     Winset(wintype);
  420.     XMoveResizeWindow(dpy, glwins[wintype], 0, 0, xsize, ysize);
  421.     glViewport(0, 0, xsize-1, ysize-1);
  422. }
  423.  
  424.  
  425.  
  426. /*  clean up before exiting
  427.  */
  428. static void clean_exit(void)
  429. {
  430.     XCloseDisplay(dpy);
  431.     exit(0);
  432. }
  433.  
  434.  
  435. /* setup all necessary GL initialzation parameters.
  436.  *  do our materials/lighting definitions, enable what we need, and define
  437.  *  the properites with gluQuadric for our two sphere objects.
  438.  */
  439. void
  440. initGL()
  441. {
  442.     GLfloat mat1_diff[] = { 0.6, 0.2, 0.2, 1.0 };
  443.     GLfloat mat1_spec[] = { 1.0, 1.0, 1.0, 1.0 };
  444.     GLfloat mat1_shin[] = { 120.0 };
  445.  
  446.     GLfloat mat2_diff[] = { 0.2, 0.8, 0.2, 1.0 };
  447.     GLfloat mat2_spec[] = { 1.0, 1.0, 1.0, 1.0 };
  448.     GLfloat mat2_shin[] = { 120.0 };
  449.  
  450.     GLfloat light_ambi[] =  { 0.2,  0.2,  0.2, 1.0 };
  451.     GLfloat light_diff[] =  { 0.9,  0.9,  0.9, 1.0 }; /* diffuse & specular */
  452.     GLfloat light_spec[] =  { 0.9,  0.9,  0.9, 1.0 }; /* are now equivalent */
  453.     GLfloat light_posi[] = { 10.0, 50.0, 50.0, 0.0 }; /* to the old LCOLOR  */
  454.  
  455.     GLfloat liteModel_ambient[] = { 0.3, 0.3, 0.3, 1.0 };
  456.  
  457.     glNewList(Mat1, GL_COMPILE);
  458.         glMaterialfv(GL_FRONT,   GL_DIFFUSE, mat1_diff);
  459.         glMaterialfv(GL_FRONT,  GL_SPECULAR, mat1_spec);
  460.         glMaterialfv(GL_FRONT, GL_SHININESS, mat1_shin);
  461.     glEndList();
  462.  
  463.     glNewList(Mat2, GL_COMPILE);
  464.         glMaterialfv(GL_FRONT,   GL_DIFFUSE, mat2_diff);
  465.         glMaterialfv(GL_FRONT,  GL_SPECULAR, mat2_spec);
  466.         glMaterialfv(GL_FRONT, GL_SHININESS, mat2_shin);
  467.     glEndList();
  468.  
  469.     glLightfv(GL_LIGHT0,  GL_AMBIENT, light_ambi);
  470.     glLightfv(GL_LIGHT0,  GL_DIFFUSE, light_diff);
  471.     glLightfv(GL_LIGHT0, GL_SPECULAR, light_spec);
  472.     glLightfv(GL_LIGHT0, GL_POSITION, light_posi);
  473.     glLightModelfv (GL_LIGHT_MODEL_AMBIENT, liteModel_ambient); 
  474.  
  475.     glEnable(GL_LIGHTING);
  476.     glEnable(GL_LIGHT0);
  477.     glEnable(GL_DEPTH_TEST);
  478.  
  479.     quadObj = gluNewQuadric();            /* used to define the two spheres */
  480.     gluQuadricDrawStyle(quadObj,GLU_FILL);  /* draw the spheres as polygons */
  481.     gluQuadricNormals(quadObj, GLU_SMOOTH);      /* make the spheres smooth */
  482.     gluQuadricOrientation(quadObj,GLU_OUTSIDE);  /* draw w/nrmls pnting out */
  483.  
  484.     buffermode = SBglwin;                 /* start out in singlebuffer mode */
  485. }
  486.  
  487.  
  488. /* define our Current Transformation Matrix
  489.  */
  490. void
  491. setMatrix()
  492. {
  493.     glMatrixMode(GL_PROJECTION);
  494.     glLoadIdentity();
  495.     glOrtho(-10.0, 10.0, -10.0*ysize/xsize, 10.0*ysize/xsize, -10.0, 10.0);
  496.     glMatrixMode(GL_MODELVIEW);
  497.     glLoadIdentity();
  498.     gluLookAt(0.0, 0.0, 4.0, 0.0, -1.5, 0.0, 0.0, 1.0, 0.0);
  499. }
  500.  
  501.  
  502.  
  503. /* draw our two spheres
  504.  */
  505. void
  506. drawScene()
  507. {
  508.     glClearColor(0.3, 0.3, 0.3, 0.0);
  509.     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  510.  
  511.     glPushMatrix();
  512.       glPushMatrix();
  513.         glCallList(Mat1);
  514.     glTranslatef(0.2, 0.2, 0.2);
  515.         gluSphere(quadObj, 3.0, 20, 20);
  516.       glPopMatrix();
  517.         glRotatef(-ax, 1.0, 0.0, 0.0);
  518.         glRotatef(ay, 0.0, 1.0, 0.0);
  519.         glRotatef(az, 0.0, 0.0, 1.0);
  520.     glTranslatef(3.4, 3.4, 0.2);
  521.         glCallList(Mat2);
  522.         gluSphere(quadObj, 4.0, 20, 20);
  523.     glPopMatrix();
  524.  
  525.     if (buffermode == DBglwin) 
  526.         glXSwapBuffers(dpy, glwins[DBglwin]);
  527.  
  528.     glFlush();
  529. }
  530.  
  531.  
  532. /* perform one loop of twirling the green sphere
  533.  */
  534. void
  535. loop()
  536. {
  537.     register int i;
  538.  
  539.     for (i=0;i<50;i++) {
  540.         ax += 5.0;
  541.         ay -= 2.0;
  542.         az += 5.0;
  543.         if (ax >=  360.0)  ax = 0.0;
  544.         if (ay <= -360.0)  ay = 0.0;
  545.         if (az >=  360.0)  az = 0.0;
  546.         drawScene();
  547.     }
  548. }
  549.